﻿namespace Deppton.Model.Xml
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Xml.Linq;

    /// <summary>
    /// Xml helper for survey reading/writing.
    /// </summary>
    public static class SurveyXmlHelper
    {
        /// <summary>
        /// Reads a survey from an Xml document.
        /// </summary>
        /// <param name="element">The Xml element.</param>
        /// <returns>The survey.</returns>
        public static Survey ReadFromElement(XElement element)
        {
            return ReadSurveyElement(element);
        }

        /// <summary>
        /// Converts a survey to an Xml document.
        /// </summary>
        /// <param name="survey">The survey.</param>
        /// <returns>The Xml document.</returns>
        public static XDocument ConvertToXDocument(Survey survey)
        {
            var document = new XDocument();

            var surveyElement = new XElement("survey");
            surveyElement.Add(new XAttribute("id", survey.Id));
            surveyElement.Add(new XAttribute("definitionId", survey.SurveyDefinitionId));
            surveyElement.Add(new XAttribute("status", survey.Status.ToString()));
            if (survey.ExpirationDate.HasValue)
            {
                surveyElement.Add("expirationDate", survey.ExpirationDate.Value);
            }

            surveyElement.Add(new XElement("description", survey.Description));

            var questionValuesElement = new XElement("questionValues");
            surveyElement.Add(questionValuesElement);
            foreach (var questionValueKey in survey.QuestionValues.Select(kv => kv.Key))
            {
                questionValuesElement.Add(new XElement("questionValue", new XAttribute("questionId", questionValueKey), survey.QuestionValues[questionValueKey].Value));
            }

            var questionsMetadataElement = new XElement("questionsMetadata");

            foreach (var questionMetadata in survey.QuestionsMetadata)
            {
                var questionElement = new XElement("question");

                questionElement.Add(new XAttribute("id", questionMetadata.QuestionId));

                foreach (var metadataKey in questionMetadata.Metadata.Select(kv => kv.Key))
                {
                    var metadataElement = new XElement(
                        "metadata",
                        new XAttribute("key", metadataKey))
                        {
                            Value = questionMetadata.Metadata[metadataKey].Value
                        };

                    questionElement.Add(metadataElement);
                }

                questionsMetadataElement.Add(questionElement);
            }

            surveyElement.Add(questionsMetadataElement);

            document.Add(surveyElement);
            return document;
        }

        /// <summary>
        /// Reads the survey Xml element.
        /// </summary>
        /// <param name="element">The survey Xml element.</param>
        /// <returns>The survey.</returns>
        private static Survey ReadSurveyElement(XElement element)
        {
            return new Survey
            {
                Id = element.Attribute("id").Value,
                ExpirationDate = element.Attribute("expirationDate") != null ? new DateTime?(Convert.ToDateTime(element.Attribute("expirationDate"), CultureInfo.InvariantCulture)) : new DateTime?(),
                Status = (SurveyStatus)Enum.Parse(typeof(SurveyStatus), element.Attribute("status").Value, true),
                Description = element.Element("description").Value,
                SurveyDefinitionId = element.Attribute("definitionId").Value,
                QuestionValues = ReadQuestionValues(element.Element("questionValues")),
                QuestionsMetadata = element.Element("questionsMetadata") != null ? ReadQuestionsMetadataElement(element.Element("questionsMetadata")) : new NamedList<QuestionMetadata>(k => k.QuestionId)
            };
        }

        /// <summary>
        /// Reads the questions metadata Xml element.
        /// </summary>
        /// <param name="questionsMetadataElement">The questions metadata Xml element.</param>
        /// <returns>The questions metadata.</returns>
        private static NamedList<QuestionMetadata> ReadQuestionsMetadataElement(XElement questionsMetadataElement)
        {
            var questionsMetadata = new NamedList<QuestionMetadata>(m => m.QuestionId);

            foreach (var questionElement in questionsMetadataElement.Elements("question"))
            {
                questionsMetadata.Add(ReadQuestionsMetadataQuestionElement(questionElement));
            }

            return questionsMetadata;
        }

        /// <summary>
        /// Reads the questions metadata's question Xml element.
        /// </summary>
        /// <param name="questionElement">The questions metadata's question Xml element.</param>
        /// <returns>The question metadata.</returns>
        private static QuestionMetadata ReadQuestionsMetadataQuestionElement(XElement questionElement)
        {
            var questionMetadata = new QuestionMetadata
            {
                QuestionId = questionElement.Attribute("id").Value
            };

            foreach (var metadataElement in questionElement.Elements("metadata"))
            {
                questionMetadata.Metadata.Add(new KeyValuePair<string, string>(metadataElement.Attribute("key").Value, metadataElement.Value));
            }

            return questionMetadata;
        }

        /// <summary>
        /// Reads the question values.
        /// </summary>
        /// <param name="element">The questions values Xml element.</param>
        /// <returns>The questions values.</returns>
        private static NamedList<KeyValuePair<string, string>> ReadQuestionValues(XElement element)
        {
            var collection = new NamedList<KeyValuePair<string, string>>(kv => kv.Key);

            if (element == null)
            {
                return collection;
            }

            foreach (var questionValueElement in element.Elements("questionValue"))
            {
                collection.Add(new KeyValuePair<string, string>(questionValueElement.Attribute("questionId").Value, questionValueElement.Value));
            }

            return collection;
        }
    }
}
